// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <ostream> // NOLINT(readability/streams)
#include <vector>

#include "src/base/platform/platform.h"
#include "src/compilation-statistics.h"

namespace v8 {
namespace internal {

    void CompilationStatistics::RecordPhaseStats(const char* phase_kind_name,
        const char* phase_name,
        const BasicStats& stats)
    {
        base::MutexGuard guard(&record_mutex_);

        std::string phase_name_str(phase_name);
        auto it = phase_map_.find(phase_name_str);
        if (it == phase_map_.end()) {
            PhaseStats phase_stats(phase_map_.size(), phase_kind_name);
            it = phase_map_.insert(std::make_pair(phase_name_str, phase_stats)).first;
        }
        it->second.Accumulate(stats);
    }

    void CompilationStatistics::RecordPhaseKindStats(const char* phase_kind_name,
        const BasicStats& stats)
    {
        base::MutexGuard guard(&record_mutex_);

        std::string phase_kind_name_str(phase_kind_name);
        auto it = phase_kind_map_.find(phase_kind_name_str);
        if (it == phase_kind_map_.end()) {
            PhaseKindStats phase_kind_stats(phase_kind_map_.size());
            it = phase_kind_map_.insert(std::make_pair(phase_kind_name_str,
                                            phase_kind_stats))
                     .first;
        }
        it->second.Accumulate(stats);
    }

    void CompilationStatistics::RecordTotalStats(size_t source_size,
        const BasicStats& stats)
    {
        base::MutexGuard guard(&record_mutex_);

        source_size += source_size;
        total_stats_.Accumulate(stats);
    }

    void CompilationStatistics::BasicStats::Accumulate(const BasicStats& stats)
    {
        delta_ += stats.delta_;
        total_allocated_bytes_ += stats.total_allocated_bytes_;
        if (stats.absolute_max_allocated_bytes_ > absolute_max_allocated_bytes_) {
            absolute_max_allocated_bytes_ = stats.absolute_max_allocated_bytes_;
            max_allocated_bytes_ = stats.max_allocated_bytes_;
            function_name_ = stats.function_name_;
        }
    }

    static void WriteLine(std::ostream& os, bool machine_format, const char* name,
        const CompilationStatistics::BasicStats& stats,
        const CompilationStatistics::BasicStats& total_stats)
    {
        const size_t kBufferSize = 128;
        char buffer[kBufferSize];

        double ms = stats.delta_.InMillisecondsF();
        double percent = stats.delta_.PercentOf(total_stats.delta_);
        double size_percent = static_cast<double>(stats.total_allocated_bytes_ * 100) / static_cast<double>(total_stats.total_allocated_bytes_);
        if (machine_format) {
            base::OS::SNPrintF(buffer, kBufferSize,
                "\"%s_time\"=%.3f\n\"%s_space\"=%" PRIuS, name, ms, name,
                stats.total_allocated_bytes_);
            os << buffer;
        } else {
            base::OS::SNPrintF(
                buffer, kBufferSize,
                "%34s %10.3f (%5.1f%%)  %10" PRIuS " (%5.1f%%) %10" PRIuS " %10" PRIuS,
                name, ms, percent, stats.total_allocated_bytes_, size_percent,
                stats.max_allocated_bytes_, stats.absolute_max_allocated_bytes_);

            os << buffer;
            if (stats.function_name_.size() > 0) {
                os << "   " << stats.function_name_.c_str();
            }
            os << std::endl;
        }
    }

    static void WriteFullLine(std::ostream& os)
    {
        os << "-----------------------------------------------------------"
              "-----------------------------------------------------------\n";
    }

    static void WriteHeader(std::ostream& os)
    {
        WriteFullLine(os);
        os << "                Turbofan phase            Time (ms)    "
           << "                   Space (bytes)             Function\n"
           << "                                                       "
           << "          Total          Max.     Abs. max.\n";
        WriteFullLine(os);
    }

    static void WritePhaseKindBreak(std::ostream& os)
    {
        os << "                                   ------------------------"
              "-----------------------------------------------------------\n";
    }

    std::ostream& operator<<(std::ostream& os, const AsPrintableStatistics& ps)
    {
        // phase_kind_map_ and phase_map_ don't get mutated, so store a bunch of
        // pointers into them.
        const CompilationStatistics& s = ps.s;

        typedef std::vector<CompilationStatistics::PhaseKindMap::const_iterator>
            SortedPhaseKinds;
        SortedPhaseKinds sorted_phase_kinds(s.phase_kind_map_.size());
        for (auto it = s.phase_kind_map_.begin(); it != s.phase_kind_map_.end();
             ++it) {
            sorted_phase_kinds[it->second.insert_order_] = it;
        }

        typedef std::vector<CompilationStatistics::PhaseMap::const_iterator>
            SortedPhases;
        SortedPhases sorted_phases(s.phase_map_.size());
        for (auto it = s.phase_map_.begin(); it != s.phase_map_.end(); ++it) {
            sorted_phases[it->second.insert_order_] = it;
        }

        if (!ps.machine_output)
            WriteHeader(os);
        for (const auto& phase_kind_it : sorted_phase_kinds) {
            const auto& phase_kind_name = phase_kind_it->first;
            if (!ps.machine_output) {
                for (const auto& phase_it : sorted_phases) {
                    const auto& phase_stats = phase_it->second;
                    if (phase_stats.phase_kind_name_ != phase_kind_name)
                        continue;
                    const auto& phase_name = phase_it->first;
                    WriteLine(os, ps.machine_output, phase_name.c_str(), phase_stats,
                        s.total_stats_);
                }
                WritePhaseKindBreak(os);
            }
            const auto& phase_kind_stats = phase_kind_it->second;
            WriteLine(os, ps.machine_output, phase_kind_name.c_str(), phase_kind_stats,
                s.total_stats_);
            os << std::endl;
        }

        if (!ps.machine_output)
            WriteFullLine(os);
        WriteLine(os, ps.machine_output, "totals", s.total_stats_, s.total_stats_);

        return os;
    }

} // namespace internal
} // namespace v8
